/*****************************************************
	Nome: 		VISED MED Module
	Versão:		3.7

	Funcionalidades:
	- Cálculo de forças
	- Integração Temporal

 *****************************************************/

#include "DEFS.H"
#include "../CORE.H"
#include "MODEL.H"
#include "MED.H"
#include "../forcas/Molas.h"
#include "../forcas/Fronteira.h"
#include "../forcas/Restricoes.h"
#include "../forcas/contato.h"
#include "../forcas/campo.h"
#include "../forcas/forcasIndependentes.h"
#include "../integracao/RungeKutta.h"
#include "../integracao/Euler.h"
#include "../integracao/Verlet.h"
#include "../colisao/Munjiza_nbs.h"
#include "../colisao/Munjiza_nbs_par.h"
#include "../colisao/Screening.h"
#include "../colisao/ScreeningParalelo.h"
#include "../colisao/ScreeningCalculos.h"
#include "../colisao/ScreeningMola.h"

#include "../colisao/Munjiza.h"


#ifdef RUNGEKUTTA_MINATO
char SPHERE::Step = 0;
#endif

void MED::Imprime(const char* TXT1,const char* TXT2)
{
    cout << AMARELO<< TXT1 << VERDE <<TXT2 << BRANCO<<"\n";
}
void MED::Imprime(const char* TXT1,const char* TXT2,const char* TXT3)
{
    cout << AMARELO<< TXT1 << VERMELHO <<TXT2 << VERDE<<TXT3 <<BRANCO<<"\n";
}

MED::MED()
{
    pCore = NULL;
    pModel = NULL;
    IsActive = false;
    min_rad = MAX_REAL;
	max_rad = 0.0;
    Critical_dT = MAX_REAL;
    qualOpcMola=3;
    tudoSeq=false;
    intSeq=false;
    detSeq=false;
    indepSeq=false;
    frontSeq=false;
    restrSeq=false;
    qualIntegracao=1;
    qualDetCol=1;
    qualDetCampo=1;
}

void MED::OrganizaPonteirosMolas()
{
    int i=0;
    int offset;
    int tamanho=0;
    pModel->threadsS[0]=pModel->SPRList.Begin;
	for (SPRING* k = pModel->SPRList.Begin; k != NULL;k = k->Next)
	{
	    tamanho++;
	}
    offset=tamanho/pModel->numProceMola;
    for (SPRING* k = pModel->threadsS[0]; k != NULL; k = k->Next)
    {
		tamanho++;
		if (tamanho == offset)
		{
            i++;
            pModel->threadsS[i] = k;
		}
	}
	ImprimeMem("Molas",tamanho*sizeof(SPRING));
}

void MED::OrganizaPonteirosEsferas(int tamanho)
{
    int i=0;
    int offset;
    offset=tamanho/pModel->numProceEsf;
    tamanho=0;
    pModel->threads[0]=pModel->SPHList.Begin;
    for (SPHERE* s = pModel->threads[0]; s != NULL; s = s->Next) {
		tamanho++;
		if (tamanho == offset)
		{
            i++;
            pModel->threads[i] = s;
		}
	}
    ImprimeMem("Esferas",tamanho*sizeof(SPHERE));
}
int MED::CalculaMenorMaiorElementos()
{
    //Abaixo aproveito o for percorrentdo toda a lista de esferas
    //para preencher os ponteiros que dividirão
    //a lista de esferas igualmente para os processadores

    //tamanho é o tamanho da lista de esferas
    int tamanho=0;

	for (SPHERE* s = pModel->SPHList.Begin; s != NULL; s = s->Next) {
		if (s->Rad < min_rad) min_rad = s->Rad;
		if (s->Rad > max_rad) max_rad = s->Rad;
		tamanho++;
	}

    //offset é a posição na lista
    //se o tamanho da lista for 10 e tivermos 2 processadores
    //um processador ficará com offset 0 e o outro 5
}
void MED::Initialize( CORE* core )
{

	if (IsActive) abort();
	pCore = core;
	pModel = &(pCore->Model);
	IsActive = true;

    OrganizaPonteirosEsferas(CalculaMenorMaiorElementos());
    OrganizaPonteirosMolas();

    //Carregando os construtores para o cálculo dos passos
    contato = new Contato();

    campoForca = new CampoForca( pModel);

    if (tudoSeq||detSeq)
    {
        if (qualDetCol==1)
        {
            //cout << "Detecção (Contato):\t\t"<< VERMELHO <<"Screening\tSequencial\n";
            Imprime("Detecção (Contato)\t\t","Screening\t","Sequencial");
            DetContatoEsferas = new Screening  ( max_rad,pModel,contato);
        }
        else if (qualDetCol==4)
        {
            Imprime("Detecção (Contato)\t\t","ScreeningCalc\t","Sequencial");
            DetContatoEsferas = new ScreeningCalculos  ( this, pModel);
        }
        else if (qualDetCol==2)
        {
            #ifndef MUNJIZA_MINATO
            printf("\nO método munjiza-minato foi desabilitado durante a compilação");
            printf("\nSe deseja habilitá-lo, defina MUNJIZA_MINATO em Munjiza.h\n");
            abort();
            #endif
            #ifdef MUNJIZA_MINATO
            cout << "Detecção (Contato): Munjiza-Minato para Contato (Sequencial)\n";
            DetContatoEsferas = new Munjiza  ( min_rad,max_rad, pModel->BoxMin, pModel->BoxMax,&pModel->SPHList,contato);
            #endif
        }
        else
        {
            Imprime("Detecção (Contato):\t\t","Munjiza-NBS\t","Sequencial");
            DetContatoEsferas = new Munjiza_nbs  ( max_rad, pModel,contato);
        }
    }
    else
    {
        if (qualDetCol==1)
        {
            Imprime("Detecção (Contato):\t\t","Screening\t","Paralelo");
            //cout << "Detecção (Contato):\t\t"<< VERMELHO <<"Screening\t"<< VERDE <<"Paralelo" << BRANCO<<"\n";
            DetContatoEsferas = new ScreeningParalelo  ( max_rad, pModel,contato);
        }
        else if (qualDetCol==2)
        {
            printf ("\nErro! Não há Munjiza-Minato em Paralelo!\n");
            printf ("Rodando a versão original...\n");

            #ifndef MUNJIZA_MINATO
            printf("\nO método munjiza-minato foi desabilitado durante a compilação");
            printf("\nSe deseja habilitá-lo, defina MUNJIZA_MINATO em Munjiza.h e recompile o programa\n");
            abort();
            #endif
            #ifdef MUNJIZA_MINATO
            //cout << "Detecção: Munjiza-Minato (Sequencial)\n";

            Imprime("Detecção (Contato):\t\t","Munjiza-Minato\t","Sequencial");
            DetContatoEsferas = new Munjiza  ( min_rad,max_rad, pModel->BoxMin, pModel->BoxMax,&pModel->SPHList,contato);
            #endif
        }
        else
        {
            Imprime("Detecção (Contato):\t\t","Munjiza-NBS\t","Paralelo");
            DetContatoEsferas = new Munjiza_nbs_par  ( max_rad, pModel,contato);
        }
    }

    if (tudoSeq||detSeq)
    {
        Imprime("Detecção (Campo de Força):\t","Screening\t","Sequencial");
        calculoCampoForca = new Screening ( pModel->FF_Range, pModel,campoForca);
    }
    else
    {
        Imprime("Detecção (Campo de Força):\t","Screening\t","Paralelo");
        calculoCampoForca = new ScreeningParalelo  ( pModel->FF_Range, pModel, campoForca);
    }

    colisaoLinhasEsferas = new Linhas (pModel,contato);

    if (tudoSeq||intSeq)
    {
        if (qualIntegracao==1)
        {
            Imprime("Integração:\t\t\t","Euler\t\t","Sequencial");
            integracao = new Euler (pModel);
        }
        else if (qualIntegracao==2)
        {
            Imprime("Integração:\t\t\t","Verlet\t\t","Sequencial");
            integracao = new Verlet (pModel);
        }
        else
        {
            #ifdef RUNGEKUTTA_MINATO
            Imprime("Integração:\t\t\t","Runge Kutta\t","Sequencial");
            integracao = new RungeKutta4ordem (pModel);
            #endif

            #ifndef RUNGEKUTTA_MINATO
            cout << VERMELHO << "Runge kutta desabilitado!\n";
            cout << "Se deseja habilitá-lo, defina RUNGEKUTTA_MINATO em RungeKutta.h e recompile o programa\n";
            cout << BRANCO;
            #endif
        }
    }
    else
    {
        if (qualIntegracao==1)
        {
            Imprime("Integração:\t\t\t","Euler\t\t","Paralelo");
            integracao = new EulerParalelo (pModel);
        }
        else if (qualIntegracao==2)
        {
            Imprime("Integração:\t\t\t","Verlet\t\t","Paralelo");
            integracao = new VerletParalelo (pModel);
        }
        else
        {
            #ifdef RUNGEKUTTA_MINATO
            Imprime("Integração:\t\t\t","Runge Kutta\t","Paralelo");
            integracao = new RungeKutta4ordemParalelo (pModel);
            #endif

            #ifndef RUNGEKUTTA_MINATO
            cout << VERMELHO << "Runge kutta desabilitado!\n";
            cout << "Se deseja habilitá-lo, defina RUNGEKUTTA_MINATO em RungeKutta.h e recompile o programa\n";
            cout << BRANCO;
            #endif
        }

    }

    if ((qualOpcMola==1)||tudoSeq)
    {
        Imprime("Cálculo de Molas:\t\t\t\t","Sequencial");
        molas = new Molas (pModel);
    }
    else if (qualOpcMola==2)
    {
        Imprime("Cálculo de Molas:\t\t","Semáforos\t","Paralelo");
        molas = new MolasParalelo (pModel);
    }
    else
    {
        Imprime("Cálculo de Molas:\t\t","Screening\t","Paralelo");
        molaScreening = new ScreeningMola (max_rad,pModel);
    }

    if (tudoSeq||indepSeq)
    {
        Imprime("Forças Independentes:\t\t\t\t","Sequencial");
        forcasIndependentes = new ForcasIndependentes (pModel);
    }
    else
    {
        Imprime("Forças Independentes:\t\t\t\t","Paralelo");
        forcasIndependentes = new ForcasIndependentesParalelo (pModel);
    }

    if (pModel->Boundary == MODEL::RIGID_BDR)
    {
        if (tudoSeq||frontSeq)
        {
            Imprime("Limites de Fronteira Rígida:\t\t\t","Sequencial");
            fronteira = new FronteiraRigida(pModel);
        }
        else
        {
            Imprime("Limites de Fronteira Rígida:\t\t\t","Paralelo");
            fronteira = new FronteiraRigidaParalelo(pModel);
        }
    }
    else //Destrutível
    {
        if (tudoSeq||frontSeq)
        {
            Imprime("Limites de Fronteira Destrutível:\t\t","Sequencial");
            fronteira = new FronteiraDestrutivel(pModel);
        }
        else
        {
            Imprime("Limites de Fronteira Destrutível:\t\t","Paralelo");
            fronteira = new FronteiraDestrutivelParalelo(pModel);
        }
    }

    if (tudoSeq||restrSeq)
    {
        Imprime("Cálculo de Restrições:\t\t\t\t","Sequencial");
        restricoes = new Restricoes(pModel);
    }
    else
    {
        Imprime("Cálculo de Restrições:\t\t\t\t","Paralelo");
        restricoes = new RestricoesParalelo(pModel);
    }
}

void MED::Terminate() {

	if (!IsActive) abort();

	IsActive = false;
}

void MED::StepForward()
{
    fronteira->calcular();
    DetContatoEsferas->calcular();
    colisaoLinhasEsferas->calcular();
    if (pModel->FF_Active) calculoCampoForca->calcular();
    forcasIndependentes->calcular();
    if (qualOpcMola!=3)molas->calcular();
    else molaScreening->calcular();
    restricoes->calcular();
    integracao->calcular();
}

void MED::StepForwardTudoNaColisao()
{

    //#pragma omp barrier
    DetContatoEsferas->calcular();
   // independentes->calcular();

    //molas->calcular();
    if (pModel->FF_Active) calculoCampoForca->calcular();
    //#pragma omp barrier
    //#pragma omp barrier
    //gravidade->calcular();
    //#pragma omp barrier
    //#pragma omp barrier
    #pragma omp single
    {
        //Para que serve esse código?? Rotacionar as linhas?
        //Qual a diferença entre um LINE e um LINE_SET?
        for (LINE_SET* ls = pModel->LINSList.Begin; ls != NULL; ls = ls->Next)
        {
            ls->Rotate( ls->W * pModel->Precision );
            ls->UpdateLines();
        }
    }
    //#pragma omp barrier
    restricoes->calcular();
    //#pragma omp barrier
    integracao->calcular();
}
